---
title: 음성 에이전트 구축
description: Learn how to build voice agents using the OpenAI Agents SDK, what features are available, how to architecture your application, and more.
---

import { Steps, Aside, Code } from '@astrojs/starlight/components';
import createAgentExample from '../../../../../../../examples/docs/voice-agents/createAgent.ts?raw';
import multiAgentsExample from '../../../../../../../examples/docs/voice-agents/multiAgents.ts?raw';
import createSessionExample from '../../../../../../../examples/docs/voice-agents/createSession.ts?raw';
import configureSessionExample from '../../../../../../../examples/docs/voice-agents/configureSession.ts?raw';
import handleAudioExample from '../../../../../../../examples/docs/voice-agents/handleAudio.ts?raw';
import defineToolExample from '../../../../../../../examples/docs/voice-agents/defineTool.ts?raw';
import toolApprovalEventExample from '../../../../../../../examples/docs/voice-agents/toolApprovalEvent.ts?raw';
import guardrailsExample from '../../../../../../../examples/docs/voice-agents/guardrails.ts?raw';
import guardrailSettingsExample from '../../../../../../../examples/docs/voice-agents/guardrailSettings.ts?raw';
import audioInterruptedExample from '../../../../../../../examples/docs/voice-agents/audioInterrupted.ts?raw';
import sessionInterruptExample from '../../../../../../../examples/docs/voice-agents/sessionInterrupt.ts?raw';
import sessionHistoryExample from '../../../../../../../examples/docs/voice-agents/sessionHistory.ts?raw';
import historyUpdatedExample from '../../../../../../../examples/docs/voice-agents/historyUpdated.ts?raw';
import updateHistoryExample from '../../../../../../../examples/docs/voice-agents/updateHistory.ts?raw';
import customWebRTCTransportExample from '../../../../../../../examples/docs/voice-agents/customWebRTCTransport.ts?raw';
import websocketSessionExample from '../../../../../../../examples/docs/voice-agents/websocketSession.ts?raw';
import transportEventsExample from '../../../../../../../examples/docs/voice-agents/transportEvents.ts?raw';
import thinClientExample from '../../../../../../../examples/docs/voice-agents/thinClient.ts?raw';
import toolHistoryExample from '../../../../../../../examples/docs/voice-agents/toolHistory.ts?raw';
import sendMessageExample from '../../../../../../../examples/docs/voice-agents/sendMessage.ts?raw';
import serverAgentExample from '../../../../../../../examples/docs/voice-agents/serverAgent.ts?raw';
import delegationAgentExample from '../../../../../../../examples/docs/voice-agents/delegationAgent.ts?raw';
import turnDetectionExample from '../../../../../../../examples/docs/voice-agents/turnDetection.ts?raw';

## 오디오 처리

기본 `OpenAIRealtimeWebRTC`와 같은 일부 전송 계층은 오디오 입력과 출력을 자동으로 처리합니다. `OpenAIRealtimeWebSocket`과 같은 다른 전송 메커니즘의 경우 세션 오디오를 직접 처리해야 합니다:

<Code lang="typescript" code={handleAudioExample} />

## 세션 구성

생성 시 [`RealtimeSession`](/openai-agents-js/openai/agents-realtime/classes/realtimesession/)에 추가 옵션을 전달하거나 `connect(...)`를 호출할 때 옵션을 전달하여 세션을 구성할 수 있습니다.

<Code lang="typescript" code={configureSessionExample} />

이 전송 계층들은 [session](https://platform.openai.com/docs/api-reference/realtime-client-events/session/update)과 일치하는 모든 매개변수를 전달할 수 있습니다.

[RealtimeSessionConfig](/openai-agents-js/openai/agents-realtime/type-aliases/realtimesessionconfig/)에 일치하는 매개변수가 없는 신규 매개변수의 경우 `providerData`를 사용할 수 있습니다. `providerData`에 전달된 내용은 `session` 객체의 일부로 직접 전달됩니다.

## 핸드오프

일반 에이전트와 마찬가지로 핸드오프를 사용해 에이전트를 여러 에이전트로 분리하고 그 사이를 오케스트레이션하여 에이전트의 성능을 향상하고 문제 범위를 더 잘 정의할 수 있습니다.

<Code lang="typescript" code={multiAgentsExample} />

일반 에이전트와 달리, Realtime Agents에서의 핸드오프는 약간 다르게 동작합니다. 핸드오프가 수행되면 진행 중인 세션이 새로운 에이전트 구성으로 업데이트됩니다. 이로 인해 에이전트는 자동으로 진행 중인 대화 기록에 접근할 수 있으며 입력 필터는 현재 적용되지 않습니다.

또한, 이는 핸드오프의 일부로 `voice` 또는 `model`을 변경할 수 없음을 의미합니다. 다른 Realtime Agents에만 연결할 수 있습니다. 다른 모델, 예를 들어 `gpt-5-mini`와 같은 추론 모델이 필요하다면 [도구를 통한 위임](#delegation-through-tools)을 사용할 수 있습니다.

## 도구

일반 에이전트와 마찬가지로 Realtime Agents는 동작을 수행하기 위해 도구를 호출할 수 있습니다. 일반 에이전트에서 사용하는 것과 동일한 `tool()` 함수를 사용하여 도구를 정의할 수 있습니다.

<Code lang="typescript" code={defineToolExample} />

Realtime Agents에서는 함수 도구만 사용할 수 있으며, 이 도구들은 Realtime Session이 실행되는 동일한 위치에서 실행됩니다. 즉, Realtime Session을 브라우저에서 실행 중이라면 도구도 브라우저에서 실행됩니다. 더 민감한 작업을 수행해야 한다면 도구 내부에서 백엔드 서버로 HTTP 요청을 보낼 수 있습니다.

도구가 실행되는 동안 에이전트는 사용자로부터 새로운 요청을 처리할 수 없습니다. 경험을 개선하는 한 가지 방법은 도구를 실행하려 할 때 이를 알리거나, 도구 실행 시간을 벌기 위해 특정 구절을 말하도록 에이전트에게 지시하는 것입니다.

### 대화 기록 접근

에이전트가 특정 도구를 호출할 때 전달된 인자 외에도, Realtime Session이 추적하는 현재 대화 기록의 스냅샷에 접근할 수 있습니다. 이는 현재 대화 상태에 따라 더 복잡한 작업을 수행해야 하거나 [도구를 통한 위임](#delegation-through-tools)을 사용할 계획이 있을 때 유용할 수 있습니다.

<Code lang="typescript" code={toolHistoryExample} />

<Aside type="note">
  전달되는 기록은 도구 호출 시점의 기록 스냅샷입니다. 사용자가 마지막으로 말한
  내용의 전사가 아직 사용 불가능할 수 있습니다.
</Aside>

### 도구 실행 전 승인

`needsApproval: true`로 도구를 정의하면, 에이전트는 도구를 실행하기 전에 `tool_approval_requested` 이벤트를 발생시킵니다.

이 이벤트를 수신하여 사용자에게 도구 호출을 승인 또는 거부할 수 있는 UI를 표시할 수 있습니다.

<Code lang="typescript" code={toolApprovalEventExample} />

<Aside type="note">
  음성 에이전트가 도구 호출에 대한 승인을 대기하는 동안에는, 에이전트는
  사용자로부터 새로운 요청을 처리할 수 없습니다.
</Aside>

## 가드레일

가드레일은 에이전트가 말한 내용이 정해진 규칙을 위반했는지 모니터링하고 즉시 응답을 차단하는 방법을 제공합니다. 이러한 가드레일 검사는 에이전트 응답의 전사에 기반하여 수행되므로, 모델의 텍스트 출력이 활성화되어 있어야 합니다(기본값으로 활성화됨).

제공한 가드레일은 모델 응답이 반환되는 동안 비동기적으로 실행되며, 예를 들어 "특정 금지어를 언급"과 같은 사전 정의된 분류 트리거를 기반으로 응답을 차단할 수 있습니다.

가드레일이 작동하면 세션은 `guardrail_tripped` 이벤트를 발생시킵니다. 이벤트에는 가드레일을 트리거한 `itemId`를 포함하는 `details` 객체도 제공됩니다.

<Code lang="typescript" code={guardrailsExample} />

기본적으로 가드레일은 100자마다 또는 응답 텍스트 생성이 끝날 때 실행됩니다. 텍스트를 말로 출력하는 데는 일반적으로 더 오래 걸리므로, 대부분의 경우 가드레일이 사용자가 듣기 전에 위반을 잡아낼 수 있습니다.

이 동작을 수정하려면 세션에 `outputGuardrailSettings` 객체를 전달할 수 있습니다.

<Code lang="typescript" code={guardrailSettingsExample} />

## 턴 감지 / 음성 활동 감지

Realtime Session은 사용자가 말할 때를 자동으로 감지하고 내장된 [Realtime API의 음성 활동 감지 모드](https://platform.openai.com/docs/guides/realtime-vad)를 사용하여 새로운 턴을 트리거합니다.

세션에 `turnDetection` 객체를 전달하여 음성 활동 감지 모드를 변경할 수 있습니다.

<Code lang="typescript" code={turnDetectionExample} />

턴 감지 설정을 수정하면 원치 않는 인터럽션(중단 처리)과 침묵 처리 보정에 도움이 됩니다. 다양한 설정에 대한 자세한 내용은 [Realtime API 문서](https://platform.openai.com/docs/guides/realtime-vad)를 확인하세요

## 인터럽션(중단 처리)

내장 음성 활동 감지를 사용할 때, 사용자가 에이전트의 말 위로 말하면 자동으로 에이전트가 이를 감지하고 말한 내용에 따라 컨텍스트를 업데이트합니다. 또한 `audio_interrupted` 이벤트를 발생시킵니다. 이는 모든 오디오 재생을 즉시 중지하는 데 사용할 수 있습니다(웹소켓 연결에만 적용).

<Code lang="typescript" code={audioInterruptedExample} />

수동 인터럽션을 수행하려면, 예를 들어 UI에 "중지" 버튼을 제공하려는 경우 `interrupt()`를 수동으로 호출할 수 있습니다:

<Code lang="typescript" code={sessionInterruptExample} />

어느 쪽이든, Realtime Session은 에이전트의 생성 중단, 사용자에게 말한 내용의 지식 단절, 기록 업데이트를 모두 처리합니다.

WebRTC로 에이전트에 연결하는 경우 오디오 출력도 지워집니다. WebSocket을 사용하는 경우, 재생 대기열에 있는 오디오의 재생을 중지하는 처리는 직접 해야 합니다.

## 텍스트 입력

에이전트에 텍스트 입력을 보내려면 `RealtimeSession`의 `sendMessage` 메서드를 사용할 수 있습니다.

이는 사용자가 에이전트와 두 가지 모달리티로 상호작용하도록 하거나 대화에 추가 컨텍스트를 제공하려는 경우에 유용합니다.

<Code lang="typescript" code={sendMessageExample} />

## 대화 기록 관리

`RealtimeSession`은 `history` 속성에서 대화 기록을 자동으로 관리합니다:

이를 사용하여 고객에게 기록을 렌더링하거나 추가 작업을 수행할 수 있습니다. 대화 중에는 이 기록이 지속적으로 변경되므로 `history_updated` 이벤트를 수신할 수 있습니다.

기록을 수정하고 싶다면, 예를 들어 메시지를 완전히 제거하거나 전사를 업데이트하려면 `updateHistory` 메서드를 사용할 수 있습니다.

<Code lang="typescript" code={updateHistoryExample} />

### 제한 사항

1. 현재로서는 사후에 함수 도구 호출을 업데이트/변경할 수 없습니다
2. 기록의 텍스트 출력에는 전사와 텍스트 모달리티가 활성화되어 있어야 합니다
3. 인터럽션으로 인해 잘린 응답에는 전사가 없습니다

## 도구를 통한 위임

![도구를 통한 위임](https://cdn.openai.com/API/docs/diagram-speech-to-speech-agent-tools.png)

대화 기록과 도구 호출을 결합하여, 보다 복잡한 작업을 수행하기 위해 대화를 다른 백엔드 에이전트에 위임한 다음 그 결과를 사용자에게 다시 전달할 수 있습니다.

<Code lang="typescript" code={delegationAgentExample} />

아래 코드는 서버에서 실행됩니다. 이 예시는 Next.js의 server actions를 통해 수행됩니다.

<Code lang="typescript" code={serverAgentExample} />
